class MVVM{
    options:any;
    $el:string;
    $data:any;
    constructor(options:any){
        this.$el = options.el;
        this.$data = options.data

        if(this.$el){
            //数据劫持
            new Observer(this.$data);
            //编译模板+数据
            new Compile(this.$el,this)
        }
    }
}

class Dep{
    Watchers:any;
    Watcher:any;
    static target:any;
    constructor(){
        this.Watchers = [];
    }
    add(watcher:any):void{
        this.Watchers.push(watcher)
    }
    notify():void{
        this.Watchers.forEach((watcher:any)=>{
            watcher.updata()
        })
    }
}
//观察者
class Watcher{
    vm:any;
    expr:string;
    cb:any;
    oldVal:string
    // Dep.target:any
    constructor(vm:any,expr:string,cb:any){
        this.vm  = vm;
        this.expr = expr;
        this.cb = cb;

        this.oldVal = this.get();
    }
    get():string{
        Dep.target= this;
        let value = compileUtil.getVal(this.expr,this.vm);
        Dep.target= null;

        return value
    }
    updata():void{
        let newVal = compileUtil.getVal(this.expr,this.vm);
        if(newVal != this.oldVal){
            this.cb(newVal)
        }
    }
}
//数据劫持
class Observer{
    data:any
    constructor(data:any){
        this.observer(data)
    }
    observer(data:any):void{
        //判断data是否存在
        if(data && typeof data === 'object'){
            for(let key in data){
                this.observerData(data,key,data[key])
            }
        }
    }
    observerData(data:any,key:string,value:any):void{
        this.observer(value);
        //实例观察
        let dep = new Dep()
        Object.defineProperty(data,key,{
            set(newVal):void{
                console.log('设置值')
                if(value != newVal){//值改变
                    value = newVal
                }
            },
            get():string{
                Dep.target && dep.add(Dep.target)
                console.log('获取值')
                return value
            }
        })
    }
}
//编译
class Compile{
    el:any;
    vm:any
    constructor(el:any,vm:any){
        this.vm = vm;
        this.el = this.isElementNode(el)? el : document.querySelector(el);

        //把html节点放到内存中
        let fragment = this.nodeToElement(this.el);
        //存到文档片段之后进行编译
        this.compile(fragment)
    }
    isElementNode(node:any):boolean{
        return node.nodeType === 1
    }
    nodeToElement(node:any):any{
        //创建一个空白文档
        let fragment = document.createDocumentFragment();
        let firstChild;
        while(firstChild = node.firstChild){
            fragment.appendChild(firstChild)
        }
        return fragment;
    }
    compile(node:any):void{
        //拿到模板的所有子节点
        let ChildNodes = node.ChildNodes;
        //子节点循环
        [...ChildNodes].forEach(item=>{
            if(this.isElementNode(item)){//是元素节点
                this.compileElement(item)
                this.compile(item)
            }else{
                this.compileText(item)
            }
        })
    }
    isDirective(node:any):boolean{
        return node.startsWith('v-')
    }
    compileElement(node:any):void{
        //拿到元素的属性
        let attributes = node.attributes;
        [...attributes].forEach(attr=>{
            let {name,value,expr} = attr;
            if(this.isDirective(name)){
                let [,directive] = name.split('-');

                compileUtil[directive](node,expr,this.vm)
            }
        })
    }
    compileText(node:any):void{
        //文本节点的内容
        let content = node.textContent;
        //判断是否含有{{}}
        if(/\{\{(.+?)\}\}/.test(content)){
            compileUtil.content(node,content,this.vm)
        }
    }

}
let compileUtil:any={
    getVal(expr:string,vm:any):string{
        return expr.split('.').reduce((pre:any,current:string)=>{
            return pre[current]
        },vm.$data)
    },
    setVal(vm:any,expr:string,value:string):any{
        expr.split('.').reduce((data:any,current:string,index:number,arr:any)=>{
            if(index === arr.length -1){
                data[current] = value;
            }
            return data[current]
        },vm.$data)
    },
    //从vm.$data中拿到 user.name的值  给node节点value 的值
    model(node:any,expr:string,vm:any):void{
        //拿值
        let value = this.getVal(expr,vm);
        //node.value = value;
        let fn = this.updater.compileModel;
        new Watcher(vm,expr,(newVal:string)=>{
            fn(node,newVal)
        })
        fn(node,value)
        node.addEventListener('input',(e:any)=>{
            this.setVal(vm,expr,e.target.value)
        })
    },
    content(node:any,content:string,vm:any):void{
        let value = content.replace(/\{\{(.+?)\}\}/g,(...argv:any)=>{
            new Watcher(vm,argv[1],(newVal:string)=>{
                fn(node,newVal)
            })
            return this.getVal(argv[1],vm)
        })
        let fn = this.updater.compileContent;
        fn(node,value)
    },
    //text
    text(node:any,expr:string,vm:any):void{
        //拿值
        let value = this.getVal(expr,vm);
        //node.value = value;
        let fn = this.updater.compiletexth;
        new Watcher(vm,expr,(newVal:string)=>{
            fn(node,newVal)
        })
        fn(node,value)
    },
    html(node:any,expr:string,vm:any):void{
        //拿值
        let value = this.getVal(expr,vm);
        //node.value = value;
        let fn = this.updater.compiletexth;
        new Watcher(vm,expr,(newVal:string)=>{
            fn(node,newVal)
        })
        fn(node,value)
    },
    updater:{
        compileModel(node:any,value:string):void{
            node.value = value
        },
        compiletexth(node:any,value:string):void{
            node.innerHtml = value
        },
        compileContent(node:any,value:string):void{
            node.textContent = value
        }      
    }
}